/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "wifi_device.h"

#include <securec.h>
#include <stdio.h>
#include <stdlib.h>

#include "wifi_device_util.h"
#include "wifi_hotspot_config.h"
#include "sys/fdcm.h"

#define WIFI_RECONN_POLICY_ENABLE 1
#define WIFI_RECONN_POLICY_TIMEOUT 0xFFFF
#define WIFI_RECONN_POLICY_PERIOD 100
#define WIFI_RECONN_POLICY_MAX_TRY_COUNT 100
#define WIFI_DISCONNECT_REASON_NO_AP 1
#define WIFI_DEFAULT_KEY_FOR_PSK "wifipskmode"
#define WLAN_STA_NAME "en1"
#define WIFI_FILE "wifi.cfg"
#define WIFI_FILE_EXIST 1
#define WIFI_FILE_UNEXIST 0

int g_wifiStaStatus = WIFI_STA_NOT_ACTIVE;
extern int g_wifiApStatus;
static WifiDeviceConfig g_wifiConfigs[WIFI_MAX_CONFIG_SIZE] = {
    { { 0 }, { 0 }, { 0 }, 0, WIFI_CONFIG_INVALID, 0, 0 }
};
static WifiEvent *g_wifiEvents[WIFI_MAX_EVENT_SIZE] = { 0 };
static int g_connectState = WIFI_STATE_NOT_AVALIABLE;
static int g_networkId = -1;
static int g_networkConfigReadFlag = 0;
static fdcm_handle_t *fdcm;
/* Flash Distribution */
#define FDCM_FLASH_START_ADDR (1996 * 1024)
#define FLASH_DEVICE_NUM 0
#define FDCM_SIZE (4 * 1024)
#define SIZE_WIFI_CONFIG (sizeof(WifiDeviceConfig) * WIFI_MAX_CONFIG_SIZE)

static WifiErrorCode WriteNetworkConfig(void)
{
    fdcm = fdcm_open(FLASH_DEVICE_NUM, FDCM_FLASH_START_ADDR, FDCM_SIZE);
    if (fdcm == NULL) {
        return ERROR_WIFI_UNKNOWN;
    }
    fdcm_write(fdcm, g_wifiConfigs, SIZE_WIFI_CONFIG);
    fdcm_close(fdcm);
    return WIFI_SUCCESS;
}

static WifiErrorCode ReadNetworkConfig(void)
{
    fdcm = fdcm_open(FLASH_DEVICE_NUM, FDCM_FLASH_START_ADDR, FDCM_SIZE);
    fdcm_read(fdcm, g_wifiConfigs, SIZE_WIFI_CONFIG);
    fdcm_close(fdcm);
    return WIFI_SUCCESS;
}

static void StaSetLocaladdr(const char *netif_name, int gw, int ipaddr,
                            int netmask)
{
    xr_wifi_set_local_ip(netif_name, gw, ipaddr, netmask);
    return;
}

static void StaSetDNSServer(int switcher)
{
    for (int i = 0; i < WIFI_MAX_DNS_NUM; i++) {
        if (switcher == XR_WIFI_EVT_CONNECTED) {
            xr_wifi_set_dns_server(
                    i, g_wifiConfigs[g_networkId].staticIp.dnsServers[i]);
        } else {
            xr_wifi_set_dns_server(i, 0);
        }
    }
    return;
}

static void StaSetWifiNetConfig(int switcher)
{
    if (switcher == XR_WIFI_EVT_CONNECTED) {
        if (g_wifiConfigs[g_networkId].ipType == DHCP) {
            xr_wifi_dhcp_enable(WLAN_STA_NAME, 1);
        } else if (g_wifiConfigs[g_networkId].ipType == STATIC_IP) {
            StaSetLocaladdr(WLAN_STA_NAME,
                            g_wifiConfigs[g_networkId].staticIp.gateway,
                            g_wifiConfigs[g_networkId].staticIp.ipAddress,
                            g_wifiConfigs[g_networkId].staticIp.netmask);
            StaSetDNSServer(XR_WIFI_EVT_CONNECTED);
            xr_wifi_netif_status_set(WLAN_STA_NAME, 1);
        }
    } else if (switcher == XR_WIFI_EVT_DISCONNECTED) {
        if (g_wifiConfigs[g_networkId].ipType == DHCP) {
            xr_wifi_dhcp_enable(WLAN_STA_NAME, 0);
            StaSetLocaladdr(WLAN_STA_NAME, 0, 0, 0);
        } else if (g_wifiConfigs[g_networkId].ipType == STATIC_IP) {
            StaSetLocaladdr(WLAN_STA_NAME, 0, 0, 0);
            StaSetDNSServer(XR_WIFI_EVT_DISCONNECTED);
            xr_wifi_netif_status_set(WLAN_STA_NAME, 0);
        }
    }
    return;
}

static void DispatchScanStateChangeEvent(const xr_wifi_event *xrEvent,
                                         const WifiEvent *hosEvent,
                                         WifiEventState event)
{
    if ((xrEvent == NULL) || (hosEvent->OnWifiScanStateChanged == NULL)) {
        return;
    }

    int size = 0;
    if (event == WIFI_STATE_NOT_AVALIABLE) {
        hosEvent->OnWifiScanStateChanged(event, size);
        return;
    }

    if (xrEvent->event == XR_WIFI_EVT_SCAN_DONE) {
        size = xrEvent->info.wifi_scan_done.bss_num;
        hosEvent->OnWifiScanStateChanged(event, size);
    }
}

static void DispatchConnectEvent(const xr_wifi_event *xrEvent,
                                 const WifiEvent *hosEvent)
{
    int cpyErr = 0;
    if (hosEvent->OnWifiConnectionChanged == NULL) {
        return;
    }

    WifiLinkedInfo info = { 0 };

    if (xrEvent->event == XR_WIFI_EVT_CONNECTED) {
        g_connectState = WIFI_STATE_AVALIABLE;
        cpyErr = memcpy_s(&info.ssid, WIFI_MAX_SSID_LEN,
                          xrEvent->info.wifi_connected.ssid,
                          XR_WIFI_MAX_SSID_LEN + 1);
        if (cpyErr != EOK) {
            printf("[wifi_service]:DispatchConnectEvent memcpy failed, err = %d\n",
                   cpyErr);
            return;
        }

        cpyErr = memcpy_s(&info.bssid, WIFI_MAC_LEN,
                          xrEvent->info.wifi_connected.bssid, WIFI_MAC_LEN);
        if (cpyErr != EOK) {
            printf("[wifi_service]:DispatchConnectEvent memcpy failed, err = %d\n",
                   cpyErr);
            return;
        }

        // StaSetWifiNetConfig(XR_WIFI_EVT_CONNECTED);
        hosEvent->OnWifiConnectionChanged(WIFI_STATE_AVALIABLE, &info);
        return;
    }

    if (xrEvent->event == XR_WIFI_EVT_STA_FCON_NO_NETWORK &&
        g_connectState == WIFI_STATE_AVALIABLE) {
        return;
    }

    info.disconnectedReason = WIFI_DISCONNECT_REASON_NO_AP;

    if (xrEvent->event == XR_WIFI_EVT_DISCONNECTED) {
        cpyErr = memcpy_s(&info.bssid, WIFI_MAC_LEN,
                          xrEvent->info.wifi_disconnected.bssid, WIFI_MAC_LEN);
        if (cpyErr != EOK) {
            printf("[wifi_service]:DispatchConnectEvent memcpy failed, err = %d\n",
                   cpyErr);
            return;
        }
        info.disconnectedReason = xrEvent->info.wifi_disconnected.reason_code;
    }

    // StaSetWifiNetConfig(XR_WIFI_EVT_DISCONNECTED);
    hosEvent->OnWifiConnectionChanged(WIFI_STATE_NOT_AVALIABLE, &info);
}

static void DispatchStaConnectEvent(const xr_wifi_event *xrEvent,
                                    const WifiEvent *hosEvent)
{
    int cpyErr = 0;
    StationInfo info = { 0 };
    if (xrEvent->event == XR_WIFI_EVT_STA_CONNECTED) {
        if (hosEvent->OnHotspotStaJoin == NULL) {
            return;
        }

        cpyErr = memcpy_s(&info.macAddress, WIFI_MAC_LEN,
                          xrEvent->info.ap_sta_connected.addr, WIFI_MAC_LEN);
        if (cpyErr != EOK) {
            printf("[wifi_service]:DispatchStaConnectEvent memcpy failed, err = %d\n",
                   cpyErr);
            return;
        }

        hosEvent->OnHotspotStaJoin(&info);
        return;
    }

    if (hosEvent->OnHotspotStaLeave == NULL) {
        return;
    }

    cpyErr = memcpy_s(&info.macAddress, WIFI_MAC_LEN,
                      xrEvent->info.ap_sta_disconnected.addr, WIFI_MAC_LEN);
    if (cpyErr != EOK) {
        printf("[wifi_service]:DispatchStaConnectEvent memcpy failed, err = %d\n",
               cpyErr);
        return;
    }
    info.disconnectedReason = xrEvent->info.ap_sta_disconnected.reason_code;
    hosEvent->OnHotspotStaLeave(&info);
}

static void DispatchApStartEvent(const WifiEvent *hosEvent)
{
    if (hosEvent->OnHotspotStateChanged == NULL) {
        return;
    }

    hosEvent->OnHotspotStateChanged(WIFI_STATE_AVALIABLE);
}

static void DispatchEvent(const xr_wifi_event *xrEvent,
                          const WifiEvent *hosEvent)
{
    switch (xrEvent->event) {
    case XR_WIFI_EVT_SCAN_DONE:
        DispatchScanStateChangeEvent(xrEvent, hosEvent, WIFI_STATE_AVALIABLE);
        break;
    case XR_WIFI_EVT_CONNECTED:
    case XR_WIFI_EVT_DISCONNECTED:
    case XR_WIFI_EVT_STA_FCON_NO_NETWORK:
        DispatchConnectEvent(xrEvent, hosEvent);
        break;
    case XR_WIFI_EVT_STA_CONNECTED:
    case XR_WIFI_EVT_STA_DISCONNECTED:
        DispatchStaConnectEvent(xrEvent, hosEvent);
        break;
    case XR_WIFI_EVT_AP_START:
        DispatchApStartEvent(hosEvent);
        break;
    default:
        /* event not supported in current version, do nothing */
        break;
    }
}

static void XrWifiWpaEventCb(const xr_wifi_event *xrEvent)
{
    if (xrEvent == NULL) {
        return;
    }
    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return;
    }
    for (int i = 0; i < WIFI_MAX_EVENT_SIZE; i++) {
        if (g_wifiEvents[i] == NULL) {
            continue;
        }
        DispatchEvent(xrEvent, g_wifiEvents[i]);
    }
    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return;
    }
}

static void RegisterXrCallback(void)
{
    int Ret = xr_wifi_register_event_callback(XrWifiWpaEventCb);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:RegisterXrCallback register callback failed\n");
    }
    Ret = xr_wifi_config_callback(1, 0, 0);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:ConfigXrCallback failed\n");
    }
}

static void UnRegisterXrCallback(void)
{
    int Ret = xr_wifi_register_event_callback(NULL);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:UnRegisterXrCallback register callback failed\n");
    }
    Ret = xr_wifi_config_callback(0, 0, 0);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:ConfigXrCallback failed\n");
    }
}

WifiErrorCode EnableWifi(void)
{
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (g_wifiStaStatus == WIFI_STA_ACTIVE) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_BUSY;
    }

    char ifName[WIFI_IFNAME_MAX_SIZE + 1] = { 0 };
    int len = sizeof(ifName);

    int Ret = xr_wifi_sta_start(ifName, &len);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:EnableWifi sta start fail\n");
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_STARTED;
    }

    // Ret = xr_wifi_sta_set_reconnect_policy(WIFI_RECONN_POLICY_ENABLE, WIFI_RECONN_POLICY_TIMEOUT,
    //     WIFI_RECONN_POLICY_PERIOD, WIFI_RECONN_POLICY_MAX_TRY_COUNT);
    // if (Ret != XR_WIFI_OK) {
    //     printf("[wifi_service]:EnableWifi set reconn policy fail\n");
    //     if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
    //         return ERROR_WIFI_UNKNOWN;
    //     }
    //     return ERROR_WIFI_UNKNOWN;
    // }

    g_wifiStaStatus = WIFI_STA_ACTIVE;

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

WifiErrorCode DisableWifi(void)
{
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (g_wifiStaStatus == WIFI_STA_NOT_ACTIVE) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_STARTED;
    }

    int Ret = xr_wifi_sta_stop();
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:DisableWifi failed to stop sta\n");
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_STARTED;
    }

    g_wifiStaStatus = WIFI_STA_NOT_ACTIVE;
    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

int IsWifiActive(void)
{
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    int ret = g_wifiStaStatus;

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    return ret;
}

WifiErrorCode Scan(void)
{
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (g_wifiStaStatus == WIFI_STA_NOT_ACTIVE) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_STARTED;
    }
    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    for (int i = 0; i < WIFI_MAX_EVENT_SIZE; i++) {
        if (g_wifiEvents[i] == NULL) {
            continue;
        }
        DispatchScanStateChangeEvent(NULL, g_wifiEvents[i],
                                     WIFI_STATE_NOT_AVALIABLE);
    }
    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    int Ret = xr_wifi_sta_scan();
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:Scan failed to start sta scan\n");
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

static xr_wifi_scan_type ScanTypeSwitch(WifiScanType type)
{
    xr_wifi_scan_type ret = XR_WIFI_BASIC_SCAN;

    switch (type) {
    case WIFI_FREQ_SCAN:
        ret = XR_WIFI_CHANNEL_SCAN;
        break;
    case WIFI_SSID_SCAN:
        ret = XR_WIFI_SSID_SCAN;
        break;
    case WIFI_BSSID_SCAN:
        ret = XR_WIFI_BSSID_SCAN;
        break;
    case WIFI_BAND_SCAN:
        ret = XR_WIFI_BASIC_SCAN;
        break;
    default:
        break;
    }

    return ret;
}

WifiErrorCode AdvanceScan(WifiScanParams *params)
{
    if (params == NULL) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (g_wifiStaStatus == WIFI_STA_NOT_ACTIVE) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_STARTED;
    }

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    for (int i = 0; i < WIFI_MAX_EVENT_SIZE; i++) {
        if (g_wifiEvents[i] == NULL) {
            continue;
        }
        DispatchScanStateChangeEvent(NULL, g_wifiEvents[i],
                                     WIFI_STATE_NOT_AVALIABLE);
    }
    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    xr_wifi_scan_params sp = { 0 };

    int cpyErr = memcpy_s(sp.ssid, sizeof(sp.ssid), params->ssid, params->ssidLen);
    if (cpyErr != EOK) {
        return ERROR_WIFI_UNKNOWN;
    }
    cpyErr = memcpy_s(sp.bssid, sizeof(sp.bssid), params->bssid,
                      sizeof(params->bssid));
    if (cpyErr != EOK) {
        return ERROR_WIFI_UNKNOWN;
    }
    sp.ssid_len = params->ssidLen;
    sp.scan_type = ScanTypeSwitch(params->scanType);
    sp.channel = FrequencyToChannel(params->freqs);

    int Ret = xr_wifi_sta_advance_scan(&sp);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:Advance Scan failed\n");
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

WifiErrorCode GetScanInfoList(WifiScanInfo *result, unsigned int *size)
{
    if (result == NULL || size == NULL || *size == 0) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    unsigned int num = WIFI_SCAN_HOTSPOT_LIMIT;

    xr_wifi_ap_info *pstResults =
            malloc(sizeof(xr_wifi_ap_info) * WIFI_SCAN_HOTSPOT_LIMIT);
    if (pstResults == NULL) {
        printf("[wifi_error]:not enough memory\n");
        return ERROR_WIFI_UNKNOWN;
    }

    int Ret = xr_wifi_sta_scan_results(pstResults, &num);
    if (Ret != XR_WIFI_OK) {
        printf("[wifi_service]:GetScanInfoList xr_wifi_sta_scan_results fail\n");
        free(pstResults);
        return ERROR_WIFI_UNKNOWN;
    }

    if (*size < num) {
        num = *size;
    }

    for (unsigned int i = 0; i < num; i++) {
        int cpyErr = memcpy_s(result[i].ssid, WIFI_MAX_SSID_LEN, pstResults[i].ssid,
                          XR_WIFI_MAX_SSID_LEN + 1);
        if (cpyErr != EOK) {
            free(pstResults);
            printf("[wifi_service]:GetScanInfoList memcpy failed, err = %d\n",
                   cpyErr);
            return ERROR_WIFI_UNKNOWN;
        }

        cpyErr = memcpy_s(result[i].bssid, WIFI_MAC_LEN, pstResults[i].bssid,
                          WIFI_MAC_LEN);
        if (cpyErr != EOK) {
            free(pstResults);
            printf("[wifi_service]:GetScanInfoList memcpy failed, err = %d\n",
                   cpyErr);
            return ERROR_WIFI_UNKNOWN;
        }

        result[i].securityType = HiSecToHoSec(pstResults[i].auth);
        result[i].rssi = pstResults[i].rssi;
        result[i].frequency = ChannelToFrequency(pstResults[i].channel);
    }

    free(pstResults);
    *size = num;

    return WIFI_SUCCESS;
}

WifiErrorCode AddDeviceConfig(const WifiDeviceConfig *config, int *result)
{
    if (config == NULL || result == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    int netId = WIFI_CONFIG_INVALID;
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    // for (int i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
    // 	if (strcmp(g_wifiConfigs[i].ssid, config->ssid) == 0) {
    // 		printf("SSID exist,no need add\n");
    // 		*result = i;
    // 		return WIFI_SUCCESS;
    // 	}
    // }

    for (int i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
        if (g_wifiConfigs[i].netId != i) {
            netId = i;
            break;
        }
    }

    if (netId == WIFI_CONFIG_INVALID) {
        printf("[wifi_service]:AddDeviceConfig wifi config is full, delete one first\n");
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_BUSY;
    }

    int cpyErr = memcpy_s(&g_wifiConfigs[netId], sizeof(WifiDeviceConfig),
                          config, sizeof(WifiDeviceConfig));
    if (cpyErr != EOK) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        printf("[wifi_service]:AddDeviceConfig memcpy failed, err = %d\n",
               cpyErr);
        return ERROR_WIFI_UNKNOWN;
    }

    g_wifiConfigs[netId].netId = netId;
    if (WriteNetworkConfig() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    *result = netId;
    return WIFI_SUCCESS;
}

WifiErrorCode GetDeviceConfigs(WifiDeviceConfig *result, unsigned int *size)
{
    if (result == NULL || size == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    unsigned int retIndex = 0;

    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (g_networkConfigReadFlag == 0) {
        g_networkConfigReadFlag = 1;
        ReadNetworkConfig();
    }

    for (int i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
        if (g_wifiConfigs[i].netId != i) {
            continue;
        }

        int cpyErr = memcpy_s(&result[retIndex], sizeof(WifiDeviceConfig),
                              &g_wifiConfigs[i], sizeof(WifiDeviceConfig));
        if (cpyErr != EOK) {
            if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
                return ERROR_WIFI_UNKNOWN;
            }
            printf("[wifi_service]:GetDeviceConfig memcpy failed, err = %d\n",
                   cpyErr);
            return ERROR_WIFI_UNKNOWN;
        }

        retIndex++;
    }

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (retIndex == 0) {
        return ERROR_WIFI_NOT_AVAILABLE;
    }

    *size = retIndex;
    return WIFI_SUCCESS;
}

static WifiErrorCode StaConnect(unsigned int chan,
                                xr_wifi_assoc_request *assocReq, int pskType)
{
    int Ret = 0;
    int lastState = g_connectState;

    g_connectState = WIFI_STATE_NOT_AVALIABLE;
    if (chan == 0) {
        Ret = xr_wifi_sta_connect(assocReq);
    } else {
        xr_wifi_fast_assoc_request fastReq = { 0 };
        fastReq.channel = chan;
        Ret += memcpy_s(&fastReq.req, sizeof(xr_wifi_assoc_request), assocReq,
                        sizeof(xr_wifi_assoc_request));
        if (pskType == WIFI_PSK_TYPE_HEX) {
            Ret += memcpy_s(fastReq.req.key, sizeof(fastReq.req.key),
                            WIFI_DEFAULT_KEY_FOR_PSK,
                            sizeof(WIFI_DEFAULT_KEY_FOR_PSK));
            Ret += memcpy_s(fastReq.psk, sizeof(fastReq.psk), assocReq->key,
                            XR_WIFI_STA_PSK_LEN);
            fastReq.psk_flag = XR_WIFI_WPA_PSK_USE_OUTER;
        }
        if (Ret != EOK) {
            printf("[wifi_service]:StaConnect memcpy failed, err = %d\n", Ret);
            g_connectState = lastState;
            return ERROR_WIFI_UNKNOWN;
        }
        Ret = xr_wifi_sta_fast_connect(&fastReq);
        if (memset_s(&fastReq, sizeof(xr_wifi_fast_assoc_request), 0,
                     sizeof(xr_wifi_fast_assoc_request)) != EOK) {
            printf("[wifi_service]:StaConnect memset failed\n");
        }
    }
    if (memset_s(assocReq, sizeof(xr_wifi_assoc_request), 0,
                 sizeof(xr_wifi_assoc_request)) != EOK) {
        printf("[wifi_service]:StaConnect memset failed\n");
    }
    if (Ret != XR_WIFI_OK) {
        g_connectState = lastState;
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

WifiErrorCode ConnectTo(int networkId)
{
    if (networkId >= WIFI_MAX_CONFIG_SIZE || networkId < 0) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    if (g_wifiStaStatus == WIFI_STA_ACTIVE) {
        if (LockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
    } else {
        return ERROR_WIFI_NOT_STARTED;
    }

    if (g_wifiConfigs[networkId].netId != networkId) {
        if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_NOT_AVAILABLE;
    }

    g_networkId = networkId;
    xr_wifi_assoc_request assocReq = { 0 };
    assocReq.auth = HoSecToHiSec(g_wifiConfigs[networkId].securityType);

    int cpyErr = memcpy_s(assocReq.ssid, sizeof(assocReq.ssid),
                          g_wifiConfigs[networkId].ssid,
                          sizeof(g_wifiConfigs[networkId].ssid));
    cpyErr += memcpy_s(assocReq.key, sizeof(assocReq.key),
                       g_wifiConfigs[networkId].preSharedKey,
                       sizeof(g_wifiConfigs[networkId].preSharedKey));
    cpyErr += memcpy_s(assocReq.bssid, sizeof(assocReq.bssid),
                       g_wifiConfigs[networkId].bssid,
                       sizeof(g_wifiConfigs[networkId].bssid));
    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (cpyErr != EOK) {
        printf("[wifi_service]:ConnectTo memcpy failed, err = %d\n", cpyErr);
        return ERROR_WIFI_UNKNOWN;
    }

    unsigned int chan = FrequencyToChannel(g_wifiConfigs[networkId].freq);
    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (StaConnect(chan, &assocReq, g_wifiConfigs[networkId].wapiPskType) !=
        WIFI_SUCCESS) {
        if (UnlockWifiEventLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_UNKNOWN;
    }
    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}

WifiErrorCode Disconnect(void)
{
    if (g_wifiStaStatus != WIFI_STA_ACTIVE) {
        return ERROR_WIFI_NOT_STARTED;
    }
    int Ret = xr_wifi_sta_disconnect();
    if (Ret != XR_WIFI_OK) {
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}

WifiErrorCode RemoveDevice(int networkId)
{
    if (networkId >= WIFI_MAX_CONFIG_SIZE || networkId < 0) {
        return ERROR_WIFI_INVALID_ARGS;
    }
    if (LockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (memset_s(&g_wifiConfigs[networkId], sizeof(WifiDeviceConfig), 0,
                 sizeof(WifiDeviceConfig)) != EOK) {
        printf("[wifi_service]:removeDevice memset failed\n");
    }
    g_wifiConfigs[networkId].netId = WIFI_CONFIG_INVALID;

    if (WriteNetworkConfig() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}

static int GetLocalWifiIp(int *const ip)
{
    return xr_wifi_get_local_ip(WLAN_STA_NAME, ip);
}

WifiErrorCode GetLinkedInfo(WifiLinkedInfo *result)
{
    if (result == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    xr_wifi_status connectStatus = { 0 };
    int Ret = xr_wifi_sta_get_connect_info(&connectStatus);
    if (Ret != XR_WIFI_OK) {
        return ERROR_WIFI_UNKNOWN;
    }

    int cpyErr = memcpy_s(result->ssid, WIFI_MAX_SSID_LEN, connectStatus.ssid,
                          XR_WIFI_MAX_SSID_LEN + 1);
    if (cpyErr != EOK) {
        printf("[wifi_service]:GetLinkedInfo memcpy failed, err = %d\n",
               cpyErr);
        return ERROR_WIFI_UNKNOWN;
    }

    cpyErr = memcpy_s(result->bssid, WIFI_MAC_LEN, connectStatus.bssid,
                      WIFI_MAC_LEN);
    if (cpyErr != EOK) {
        printf("[wifi_service]:GetLinkedInfo memcpy failed, err = %d\n",
               cpyErr);
        return ERROR_WIFI_UNKNOWN;
    }

    if (connectStatus.status == XR_WIFI_CONNECTED) {
        result->connState = WIFI_CONNECTED;
        result->rssi = xr_wifi_sta_get_ap_rssi();
    } else {
        result->connState = WIFI_DISCONNECTED;
    }

    if (GetLocalWifiIp(&(result->ipAddress)) != EOK) {
        return ERROR_WIFI_UNKNOWN;
    }

    return WIFI_SUCCESS;
}

WifiErrorCode RegisterWifiEvent(WifiEvent *event)
{
    if (event == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    int emptySlot = WIFI_CONFIG_INVALID;

    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    for (int i = 0; i < WIFI_MAX_EVENT_SIZE; i++) {
        if (g_wifiEvents[i] == event) {
            if (UnlockWifiEventLock() != WIFI_SUCCESS) {
                return ERROR_WIFI_UNKNOWN;
            }
            return ERROR_WIFI_INVALID_ARGS;
        }

        if (g_wifiEvents[i] != NULL) {
            continue;
        }

        emptySlot = i;
        break;
    }

    if (emptySlot == WIFI_CONFIG_INVALID) {
        if (UnlockWifiEventLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return ERROR_WIFI_BUSY;
    }

    g_wifiEvents[emptySlot] = event;
    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    RegisterXrCallback();

    return WIFI_SUCCESS;
}

WifiErrorCode UnRegisterWifiEvent(const WifiEvent *event)
{
    if (event == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    if (LockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }

    for (int i = 0; i < WIFI_MAX_EVENT_SIZE; i++) {
        if (g_wifiEvents[i] != event) {
            continue;
        }

        g_wifiEvents[i] = 0;

        if (UnlockWifiEventLock() != WIFI_SUCCESS) {
            return ERROR_WIFI_UNKNOWN;
        }
        return WIFI_SUCCESS;
    }

    if (UnlockWifiEventLock() != WIFI_SUCCESS) {
        return ERROR_WIFI_UNKNOWN;
    }
    return ERROR_WIFI_UNKNOWN;
}

WifiErrorCode GetDeviceMacAddress(unsigned char *result)
{
    if (result == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }

    int Ret = xr_wifi_get_macaddr((char *)result, WIFI_MAC_LEN);
    if (Ret != XR_WIFI_OK) {
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}
